package net.aramzamzam.tapestry.judo.components;

import java.util.Set;

import net.aramzamzam.tapestry.judo.services.Require;
import net.aramzamzam.tapestry.judo.services.WidgetService;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.FieldValidationSupport;
import org.apache.tapestry5.FieldValidator;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.ValidationException;
import org.apache.tapestry5.ValidationTracker;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.SupportsInformalParameters;
import org.apache.tapestry5.corelib.base.AbstractField;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.ComponentDefaultProvider;
import org.apache.tapestry5.services.FieldValidatorDefaultSource;
import org.apache.tapestry5.services.Request;



/**
 * A component used to collect a provided date from the user using a client-side JavaScript calendar. Non-JavaScript
 * clients can simply type into a text field.
 * <p/>
 * One wierd aspect here is that, because client-side JavaScript formatting and parsing is so limited, we (currently)
 * use Ajax to send the user's input to the server for parsing (before raising the popup) and formatting (after closing
 * the popup).  Wierd and inefficient, but easier than writing client-side JavaScript for that purpose.
 */
// TODO: More testing; see https://issues.apache.org/jira/browse/TAPESTRY-1844
@Require("dijit.form.DateTextBox")
@SupportsInformalParameters
public abstract class ConstraintsTextBox<T> extends AbstractField
{

	/**
     * The object that will perform input validation (which occurs after translation). The translate binding prefix is
     * generally used to provide this object in a declarative fashion.
     */
    @Parameter(defaultPrefix = BindingConstants.VALIDATE)
    private FieldValidator<Object> validate;
    
    @Parameter(required = true, principal = true, autoconnect = true)
    private T value;


    @Environmental
    private ValidationTracker tracker;

    @Inject
    private ComponentResources resources;

    @Inject
    private Request request;

    @Inject
    private ComponentDefaultProvider defaultProvider;

    @Inject
    private FieldValidationSupport fieldValidationSupport;
    
    @Environmental
	private WidgetService dojoPageRenderSupport;
    
    private static final String INPUT_PARAMETER = "input";
    
    private static final String CONSTRAINTS_ATTRIBUTE = "constraints";
    
    /**
     * Computes a default value for the "validate" parameter using {@link FieldValidatorDefaultSource}.
     */
    @SuppressWarnings("unchecked")
	final FieldValidator defaultValidate()
    {
        return defaultProvider.defaultValidator("value", resources);
    }

    void beginRender(MarkupWriter writer)
    {
        String value = tracker.getInput(this);

        if (value == null) value = getClientValue(this.value);

        String clientId = getClientId();
        
        String sConstraints = resources.getInformalParameter(CONSTRAINTS_ATTRIBUTE, String.class);
        JSONObject jContraints=getConstraints();
        if (sConstraints!=null)
        {
        	JSONObject js = new JSONObject(sConstraints);
        	Set<String> keys = js.keys();
        	for (String key : keys) {
        		jContraints.put(key, js.get(key));
			}
        }

        Element ele=writer.element(INPUT_PARAMETER,

                       "type", "text",

                       "name", getControlName(),

                       "id", clientId,
                       
                       "value", value);

        writeDisabled(writer);

        validate.render(writer);

        resources.renderInformalParameters(writer);

        writer.end();
        dojoPageRenderSupport.createWidget(getDojoType(), ele, resources, CONSTRAINTS_ATTRIBUTE,jContraints.toString());
    }

    private void writeDisabled(MarkupWriter writer)
    {
        if (isDisabled()) writer.attributes("disabled", "disabled");
    }


    @Override
    protected void processSubmission(String elementName)
    {
        String value = request.getParameter(elementName);

        tracker.recordInput(this, value);

        try
        {
        	T parsedValue = getServerValue(value);
        	fieldValidationSupport.validate(parsedValue, resources, validate);
        	this.value=parsedValue;
        }
        catch (ValidationException ex)
        {
            tracker.recordError(this, ex.getMessage());
        }
    }

    void injectResources(ComponentResources resources)
    {
        this.resources = resources;
    }

    @Override
    public boolean isRequired()
    {
        return validate.isRequired();
    }
    
    protected abstract JSONObject getConstraints();
    protected abstract String getDojoType();
    protected abstract String getClientValue(T value);
    protected abstract T getServerValue(String value) throws ValidationException;

}
